from PageParsingTypes import *
import re
import urllib
import inspect
import Util

from BeautifulSoup import BeautifulSoup

# Setup soup constants
TAG_CLASSNAME, STRING_CLASSNAME = None, None
def setupSoupConstants():
	global TAG_CLASSNAME, STRING_CLASSNAME
	html = '<b>Test</b>'
	soup = BeautifulSoup(html)
	TAG_CLASSNAME = str(soup.b.__class__)
	STRING_CLASSNAME = str(soup.b.string.__class__)
setupSoupConstants()

class Return:
	pass

	def __str__(self):
		return self.__longString()
	
	def __repr__(self):
		return self.__shortString()
	
	def getExceptions(self):
		class NothingClass:
			pass
		nothingInstance = NothingClass()
		exemptions = tuple(dir(nothingInstance))
		return exemptions
		
	def __longString(self):		
		return str(self.__dict__)
	
	def __shortString(self):
		return str(self.__dict__.keys())

def checkTable(table):
	results = Return()
	results.stringElementCount = 0
	results.goodTagCount = 0
	results.badTagCount = 0
	results.badTags = []
	results.wtf = []
	
	for element in table.contents:
		if str(element.__class__) == STRING_CLASSNAME:
			results.stringElementCount += 1
		elif str(element.__class__) == TAG_CLASSNAME:
			if element.name == 'tr':
				results.goodTagCount += 1
			else:
				results.badTagCount += 1
				results.badTags.append(element)
		else:
			results.wtf.append(element)
	return results

# Note: Never mind the following note. Won't work, since all editions for a CD
# share the same torrent class code. Try instead verifying a row by searching
# for the torrent download link itself, for example. Figure out what album and
# edition a torrent belongs to passively, by ordering, like you've been doing.

# Todo: Use this function to search the whole table for matches on the exact
# torrent class (of format groupid_673942) that was located in the edition
# row. Then attempt to process all those as torrent matches. Use the
# tag.findAll(lambda that takes a tag) method.

def findInClassLambda(name, value):
	def f(tag):
		attr = tag.get(name)
		if attr is None:
			return False
		return value in attr.split()
	return f

def elementIsInClass(element, cls):
	return cls in element.get('class').split()

'Get links contained in tag that contain text in the URL.'
def getLinksWhereUrlContains(text):
	def func(tag):
		if tag.name != 'a':
			return False
		for k, v in tag.attrs:
			if k == 'href' and text in v:
				return True
		return False
	return func

def verifyRowContents(row, restrictions, verbose=False):
	for restriction in restrictions:
		if not restriction.apply(row):
			print 'Restriction %s failed! (Input: %s)' % (restriction, list(row)[restriction.index])
			# Restriction is failing because we're running this against the entire <tr> instead of just the <td colspan=2> bit
			if verbose:
				verbosePrintRow(row)
			return False
	return True

GET_FREEFORM_ARTIST_RESTRICTIONS = (
	Restriction(0, Lambda('lambda x: str(x.__class__) == "' + STRING_CLASSNAME + '"')),
	Restriction(0, Lambda("lambda x: str(x).endswith(' - ')"))
)

# Don't worry about this. I ended up using a different framework to verify
# the format of the year.
GET_YEAR_RESTRICTIONS = (
	
)

def verbosePrintRow(row):
	print 'Verbose data regarding row follows:'
	for element in row:
		print '(%s): %s' % (str(element.__class__), str(element))
	print 'Verbose output ends'

def createAlbumTagMatchFunction(albumTag):
	def function(x):
		return x == albumTag
	return function

def generateArtistsFromTags(tags, albumInfo, albumTag):
	if len(tags) == 0:
		# Make sure that the row matches our expectation for the format of a row.
		# First item is NavigableString with free-form artist info.
		# Second item is Tag with 
		artistRestrictions = list(GET_FREEFORM_ARTIST_RESTRICTIONS)
		manualLambda = createAlbumTagMatchFunction(albumTag)
#		artistRestrictions.append(Restriction(1, Lambda('lambda x: x == albumTag')))
		artistRestrictions.append(Restriction(1, manualLambda))
		success = verifyRowContents(albumInfo, artistRestrictions, verbose=True)
		if not success:
			print 'Literal:', urllib.quote(str(list(albumInfo)[0]))
			print 'Could not verify structure for artist extraction:', albumTag.string
		else:
			artist = Artist()
			artist.name = str(list(albumInfo)[0]).strip()[:-2]
			print 'Manually extracted artist name: "%s"' % (artist.name,)
			return [artist]
	artists = []
	for tag in tags:
		artist = Artist()
		artist.url = tag.get('href')
		artist.name = tag.string
		artists.append(artist)
	return artists
		
def parseAlbumHeader(row, albumId=None):
	matches = row.findAll('td', attrs={'colspan':'2'})
	assert(len(matches) == 1)
	albumInfo = matches[0]
	
	if albumId is not None and albumId == injectionIndex:
		print 'Injection being performed'
		global injectedValue
		injectedValue = albumInfo
		print 'Injection complete'
	
	artistTags = albumInfo.findAll('a', attrs={'href':re.compile('artist.php')})
	albumTags = albumInfo.findAll('a', attrs={'href':re.compile('torrents.php'), 'title':'View Torrent'})
	assert(len(albumTags) == 1)

	albumTag = albumTags[0]
	artists = generateArtistsFromTags(artistTags, albumInfo=albumInfo, albumTag=albumTag)	

	album = Album()
	album.artists = artists
	album.url = albumTag.get('href')
	album.name = albumTag.string
	album.year = getYear(albumInfo, albumTag)
	
	return album

def getYear(albumInfo, albumTag):
	albumInfoList = list(albumInfo)
	albumTagIndex = albumInfoList.index(albumTag)
	yearNavString = albumInfoList[albumTagIndex + 1]
	# Check the format of the year string
	isCorrectClass = str(yearNavString.__class__) == STRING_CLASSNAME
	numericalPattern = re.compile(r'\[([0-9]+)\]')
	stringPattern = re.compile(r'\[(.*?)\]')
	yearString = yearNavString.string.strip()
	
	match = re.match(numericalPattern, yearString)
	if match is not None:
		return int(match.groups()[0])
	
	match = re.match(stringPattern, yearString)
	if match is not None:
		return match.groups()[0]
	
	return None

injectedValue = None
injectionIndex = None

def parseTable(table):
	albumIndex = 0
	albums = []
	
	albumTorrents = []
	plainTorrents = []
	badRows = []
	
	rows = removeStringsFromTable(table)[1:]
	currentAlbum = None
	currentEdition = None
	for row in rows:
		if elementIsInClass(row, 'group'):
			# This is a new album group. Reset whatever is necessary.
			currentAlbum = None
			if injectionIndex is None:
				currentAlbum = parseAlbumHeader(row)
			else:
				currentAlbum = parseAlbumHeader(row, albumId=albumIndex)
#			currentAlbum = parseAlbumHeader(row)
			albums.append(currentAlbum)
			albumIndex += 1
		elif elementIsInClass(row, 'group_torrent'):
			if isEditionRow(row):
				editionName = getEdition(row)
#				print 'Processing edition row'
				currentEdition = Edition()
				currentEdition.name = editionName
#				currentEdition.torrentId = getTorrentId
				currentEdition.album = currentAlbum
				currentAlbum.editions.append(currentEdition)
			else:
				currentEdition.rawTorrentRows.append(row)
#				albumTorrents.append(row)
		elif elementIsInClass(row, 'torrent'):
			plainTorrents.append(row)
		else:
			badRows.append(row)
	r = Return()
	r.albums = albums
	r.albumTorrents = albumTorrents
	r.plainTorrents = plainTorrents
	r.badRows = badRows
	return r

def handleATRows(results):
	for album in results.albums:
		for edition in album.editions:
			assert(len(edition.torrents) == 0)
			print '%s - %s (%d)' % (album, edition, len(edition.rawTorrentRows))
			for row in edition.rawTorrentRows:
				torrent = parseAlbumTorrentRow(row)
				torrent.edition = edition
				edition.torrents.append(torrent)
			edition.rawTorrentRows = []

def parseAlbumTorrentRow(row):
	torrent = Torrent()
	tds = tuple(row.findAll('td'))
	tds = tuple([x for x in tds if x != '\n'])
	assert(len(tds) == 7)
	assert(tds[0].get('colspan') == '3')
	for i, element in enumerate(tds):
		if i == 0:
			if not element.get('colspan') == '3':
				# Redundant now
				print 'Could not verify colspan attribute on first item.'
			else:
				parseLinkTd(row=element, torrent=torrent)
				# Deal with properly formatted first item.
				pass
		elif i == 1:
			torrent.fileCount = int(element.string)
		elif i == 2:
			# You get something of this format
			# <td class="nobr"><span title="Jan 09 2010, 23:17">6 hours ago</span></td>
			# Once you figure out the role of the span title (current
			# time, or target time? Time zone?) you could maybe
			# construct useful time and timedelta objects.
			timestamp = Util.parseWhatTimestamp(element.span.get('title'))
			torrent.time = timestamp
#				text = element.span.string
#				convertedDate = timestamp.astimezone(Util.Pacific())
#				stringDate = convertedDate.strftime('%d %b %Y %I:%M:%S %p %Z')
#				print "'%s' -> '%s'" % (stringDate, text)
		elif i == 3:
			torrent.size = Size(element.string)
		elif i == 4:
			# Snatches
			torrent.snatches = int(element.string)
		elif i == 5:
			# Seeders
			torrent.seeders = int(element.string)
		elif i == 6:
			# Leechers
			torrent.leechers = int(element.string)
		else:
			print '\tCan\'t happen! (%d)' % (len(at.findAll('td')),), element
#			print '*', element
	return torrent

def parseLinkTd(row, torrent):
	# Grab the informational link for the torrent itself
	# Grab the text of the informational link that contains useful format data for the torrent
	# Grab the download link of the torrent
	downloadLinks = row.span.findAll('a', title='Download')
	assert(len(downloadLinks) == 1)
	torrent.downloadLink = downloadLinks[0].get('href')
	spanLinks = row.span.findAll('a')
	allLinks = row.findAll('a')
	nonSpanLinks = [x for x in allLinks if x not in spanLinks]
	assert(len(nonSpanLinks) == 1)
	informationLink = nonSpanLinks[0]
	torrent.informationLink = informationLink.get('href')
	torrent.formatInformation = informationLink.string

def removeStringsFromTable(table):
	arr = []
	for element in table.contents:
		try:
			element.attrs
			arr.append(element)
		except AttributeError:
			pass
	return arr

def isEditionRow(row):
	goodTds = row.findAll('td', attrs={'class':'edition_info'})
	return len(goodTds) == 1

def getEdition(row):
	td = row.findAll('td', attrs={'class':'edition_info'})[0]
	currentElement = td
	while str(currentElement.__class__) == TAG_CLASSNAME:
		assert(len(currentElement) == 1)
		currentElement = tuple(currentElement)[0]
	assert(str(currentElement.__class__) == STRING_CLASSNAME) # Found the string
	return str(currentElement)
